#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "../comm/log.hpp"
#include "../comm/util.hpp"

namespace ns_runner
{
    using namespace ns_log;
    using namespace ns_util;

    class Runner
    {
    public:
        // 设置cpu时间限制和内存使用限制
        static void SetProcLimit(int _cpu_limit, int _mem_limit)
        /*                           单位秒             单位kb        */
        {
            //std::cout << "设置时间限制和内存使用限制" << std::endl;
            
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            cpu_rlimit.rlim_cur = _cpu_limit;
            setrlimit(RLIMIT_CPU, &cpu_rlimit); // 设置cpu限制

            struct rlimit mem_rlimit;
            mem_rlimit.rlim_max = RLIM_INFINITY;
            mem_rlimit.rlim_cur = _mem_limit * 1024;
            setrlimit(RLIMIT_AS, &mem_rlimit); // 设置内存限制
        }

    public:
        // 只运行文件，关心是否异常,不关心文件运行结果。
        // 返回0表示运行正常
        // 返回<0表示某些错误
        // 返回>0表示子进程的退出码，用来让oj明白具体错误
        static int Run(std::string &file_name, int cpu_limit, int mem_limit)
        {
            umask(0);
            // 运行文件需要标准流
            int _stdin_fd = open(PathUtil::Stdin(file_name).c_str(), O_CREAT | O_RDONLY, 0644);
            int _stdout_fd = open(PathUtil::Stdout(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
            int _stderr_fd = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
            
            if (_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                Log(ERROR) << "运行时打开标准文件失败"
                           << "\n";
                return -1;
            }
            pid_t id = fork();
            // 子进程需要重定向标准流，同时运行可执行程序
            if (id == 0)
            {
                //std::cout << "准备进行重定向，设置限制，调用函数去跑生成的exe程序" << std::endl;
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);
                // 设置限制，防止时空越界
                SetProcLimit(cpu_limit, mem_limit);

                execl(PathUtil::Exe(file_name).c_str(), PathUtil::Exe(file_name).c_str(),
                       nullptr);
                exit(1);
            }
            // 父进程需要关闭fd，同时等待子进程退出，返回子进程的退出码。
            else if (id > 0)
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int staus;
                waitpid(id, &staus, 0);
                Log(INFO) << "程序运行完毕,info : " << (staus & 0x7F) << "\n";
                return (staus & 0x7F);
            }
            else
            {
                Log(ERROR) << "创建子进程失败"
                           << "\n";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2;
            }
        }
    };
}